﻿Imports System.Reflection

<ComClass(ClsCircle.ClassId, ClsCircle.InterfaceId, ClsCircle.EventsId)>
Public Class ClsCircle

#Region "COM GUID"
    ' 这些 GUID 提供此类的 COM 标识 
    ' 及其 COM 接口。若更改它们，则现有的
    ' 客户端将不再能访问此类。
    Public Const ClassId As String = "74717f2e-1b60-4eba-898f-e0b065be9fdf"
    Public Const InterfaceId As String = "0a034bf3-d279-48ef-9850-db4d6a480134"
    Public Const EventsId As String = "21e10980-0fe4-4921-ac19-da1dd21f346b"
#End Region

    ' 可创建的 COM 类必须具有一个不带参数的 Public Sub New() 
    ' 否则， 将不会在 
    ' COM 注册表中注册此类，且无法通过
    ' CreateObject 创建此类。
    Public Sub New()
        MyBase.New()
    End Sub
#Region "字段和属性"
    Private _ce As ClsPoint2d
    Public Property Center() As ClsPoint2d
        Get
            Return _ce
        End Get
        Set(ByVal value As ClsPoint2d)
            _ce = value
        End Set
    End Property

    Private _r As Double
    Public Property Radius() As Double
        Get
            Return _r
        End Get
        Set(ByVal value As Double)
            _r = value
        End Set
    End Property
    Public ReadOnly Property Area As Double
        Get
            Return Math.PI * Me.Radius * Me.Radius
        End Get
    End Property
    Public ReadOnly Property Circumference As Double
        Get
            Return 2 * Math.PI
        End Get
    End Property

    Public ReadOnly Property EquationOfCircle As String
        Get
            Return String.Format("(x-({0}))^2+(y-({1}))^2 = {2}^2", Me.Center.X, Me.Center.Y, Me.Radius)
        End Get
    End Property

    Public ReadOnly Property BoundingBox As ClsRectangle
        Get
            Dim rec As New ClsRectangle
            rec.GravityCenter = Me.Center
            rec.AngleToXails = 0
            rec.Height = Me.Radius * 2
            rec.Width = Me.Radius * 2
            Return rec
        End Get
    End Property
#End Region

#Region "构造函数"
    Public Sub CreateByCenterAndRadius(ce As ClsPoint2d, r As Double)
        Me.Center = ce
        Me.Radius = r
    End Sub

    Public Sub CreateBy3Pts(a As ClsPoint2d, b As ClsPoint2d, c As ClsPoint2d)
        Dim tri As New ClsTriangle
        tri.CreatorByPoints(a, b, b)
        Me.Center = tri.CircumcircleCenter
        Me.Radius = tri.CircumcircleRadius
    End Sub

#End Region

#Region "与cad转化"
    Public Function CreateFromAcadObject(acCir As Object) As Boolean
        If TypeName(acCir) = "IAcadCircle" Then
            Dim p As New ClsPoint2d
            p.CreatorByArray(acCir.Center)
            Me.Center = p
            Me.Radius = acCir.Radius
            Return True
        Else
            Return False
        End If
    End Function

    Public Function ToAcadEntity(acLayout As Object, normal As ClsPlane) As Object
        Dim acCir As Object = acLayout.AddCircle(Me.Center.To3DArray(normal.Original.Z), Me.Radius)
        acCir.Normal = normal.Normal.ToArray()
        Return acCir
    End Function
#End Region

#Region "com 不可见的内容"

    Public Sub New(ce As ClsPoint2d, r As Double)
        Call CreateByCenterAndRadius(ce, r)
    End Sub

    Public Sub New(a As ClsPoint2d, b As ClsPoint2d, c As ClsPoint2d)
        Call CreateBy3Pts(a, b, c)
    End Sub

    Public Overrides Function ToString() As String
        Return Me.EquationOfCircle
    End Function
#End Region

#Region "圆和圆的位置关系"

    Public Function RealtionToCircle(cir As ClsCircle) As 圆与圆的位置关系
        Dim d = Me.Center.GetDistanceTo(cir.Center) '圆心距离
        Dim r1 = Me.Radius
        Dim r2 = cir.Radius

        If d - r1 + r2 > 0.00000001 Then
            Return 圆与圆的位置关系.外离
        ElseIf Math.Abs(d - r1 + r2) <= 0.00000001 Then
            Return 圆与圆的位置关系.外切
        ElseIf d - Math.Abs(r1 - r2) > 0.00000001 And d - r1 - r2 > 0.00000001 Then
            Return 圆与圆的位置关系.相交
        ElseIf d - Math.Abs(r1 - r2) <= 0.00000001 Then
            Return 圆与圆的位置关系.内切
        Else
            Return 圆与圆的位置关系.内含
        End If
    End Function
#End Region

#Region "求交点"
    Public Function GetCrossPointsToCircel(cir As ClsCircle) As ClsPoint2d()
        Dim rtn As New List(Of ClsPoint2d)
        Dim vce = Me.Center.GetVector2dTo(cir.Center) '2个圆心连线的向量
        Select Case Me.RealtionToCircle(cir)
            Case 圆与圆的位置关系.内切, 圆与圆的位置关系.外切
                '一个点
                rtn.Add(Me.Center.Add(vce.UnitVector.Multiply(Me.Radius)))
            Case 圆与圆的位置关系.内含, 圆与圆的位置关系.外离
                '无交点

            Case Else
                '两交点
                '求公切线与的夹角值
                '交点,和两圆心构成的三角形中,设A角为圆心连线对应的角,a点(x,y)为交点,设B角为当前圆圆心与交点连线对应的角,b(x2,y2)为另外一个圆的圆心
                '设C角为另外一个圆的圆心与交点连线对应的角,c(x2,y2)为当前圆的圆心

                Dim A = vce.Length, B = Me.Radius, C = cir.Radius

                Dim pb = cir.Center, pc = Me.Center

                Dim angA = Math.Acos((B ^ 2 + C ^ 2 - A ^ 2) / (2 * B * C))

                Dim angB = Math.Acos((A ^ 2 + C ^ 2 - B ^ 2) / (2 * A * C))

                Dim angC = Math.PI - angA - angB
                Dim vca = vce.Rotate(angC).UnitVector.Multiply(B)

                rtn.Add(pc.Add(vca))

                Dim vcb = vce.Rotate(angB).UnitVector.Multiply(C)

                rtn.Add(pb.Subtract(vcb))

        End Select
        Return rtn.ToArray()
    End Function

    ''' <summary>
    ''' 从圆外一点给圆作切线,求切点坐标
    ''' </summary>
    ''' <param name="pt">圆外一点</param>
    ''' <returns>切点坐标</returns>
    Public Function GetTangentPointsFromPoint(pt As ClsPoint2d) As ClsPoint2d()

        Dim rtn As New List(Of ClsPoint2d)

        If pt.PointRelation2Circle(Me) = 点与封闭图形的位置关系.在外部 Then

            Dim vcept = Me.Center.GetVector2dTo(pt) '圆心到圆外一点的向量,

            Dim ang = Math.Acos(Me.Radius / vcept.Length) '求圆心与切点的连线与圆心到圆外一点连线的夹角

            Dim vcetangent1 = vcept.UnitVector.Rotate(ang).Multiply(Me.Radius)

            Dim vcetangent2 = vcept.UnitVector.Rotate(-ang).Multiply(Me.Radius)

            rtn.AddRange({Me.Center.Add(vcetangent1), Me.Center.Add(vcetangent2)})

        End If

        Return rtn.ToArray()

    End Function


    Public Function GetExternalCommontangentLine(cir As ClsCircle) As ClsLine2d()

        Dim rtn As New List(Of ClsLine2d)

        Select Case Me.RealtionToCircle(cir)
            Case 圆与圆的位置关系.外离, 圆与圆的位置关系.外切, 圆与圆的位置关系.相交
                '2条外公切线
                Dim tempCir As New ClsCircle With {
                    .Center = If(Me.Radius > cir.Radius, Me.Center, cir.Center),
                    .Radius = Math.Abs(Me.Radius - cir.Radius)}

                Dim vcept = tempCir.Center.GetVector2dTo(If(Me.Radius > cir.Radius, cir.Center, Me.Center)) '圆心到圆外一点的向量,

                Dim ang = Math.Acos(tempCir.Radius / vcept.Length) '求圆心与切点的连线与圆心到圆外一点连线的夹角

                Dim vcetangent1 = vcept.UnitVector.Rotate(ang)

                rtn.Add(New ClsLine2d With {.StartPoint = Me.Center.Add(vcetangent1.Multiply(Me.Radius)),
                        .EndPoint = cir.Center.Add(vcetangent1.Multiply(cir.Radius))})

                Dim vcetangent2 = vcept.UnitVector.Rotate(-ang)

                rtn.Add(New ClsLine2d With {.StartPoint = Me.Center.Add(vcetangent2.Multiply(Me.Radius)),
                        .EndPoint = cir.Center.Add(vcetangent2.Multiply(cir.Radius))})

            Case Else

        End Select

        Return rtn.ToArray()

    End Function

    Public Function GetInnerCommontangentLine(cir As ClsCircle) As ClsLine2d()
        Dim rtn As New List(Of ClsLine2d)

        Select Case Me.RealtionToCircle(cir)
            Case 圆与圆的位置关系.外离

                Dim vcemecir = Me.Center.GetVector2dTo(cir.Center)

                '内公切线的交点
                Dim cp = Me.Center.Add(vcemecir.Multiply(Me.Radius / (Me.Radius + cir.Radius)))

                Dim ang = Math.Acos(Me.Radius / cp.GetVector2dTo(Me.Center).Length) '

                Dim vcetangent = vcemecir.UnitVector.Rotate(ang)

                Dim vcetangent1 = vcemecir.UnitVector.Rotate(-ang)

                Dim l1 As New ClsLine2d With {
                    .StartPoint = Me.Center.Add(vcetangent.Multiply(Me.Radius)),
                    .EndPoint = cir.Center.Add(vcetangent.NegateVector.Multiply(cir.Radius))
                    }

                Dim l2 As New ClsLine2d With {
                   .StartPoint = Me.Center.Add(vcetangent1.Multiply(Me.Radius)),
                   .EndPoint = cir.Center.Add(vcetangent1.NegateVector.Multiply(cir.Radius))
                   }

                rtn.AddRange({l1, l2})
            Case Else

        End Select

        Return rtn.ToArray()
    End Function
#End Region

#Region "Clone"
    Public Function Clone() As ClsCircle

        Dim t As Type = Me.GetType()
        Dim setProps As List(Of PropertyInfo) = t.GetProperties.Where(Function(p) p.CanWrite).ToList()
        Dim rtnObj = Activator.CreateInstance(t)
        For Each p As PropertyInfo In setProps
            p.SetValue(rtnObj, p.GetValue(Me, Nothing), Nothing)
        Next
        Return DirectCast(rtnObj, ClsCircle)

    End Function

#End Region

#Region "几何线性变换"
    ''' <summary>
    ''' 旋转当前圆
    ''' </summary>
    ''' <param name="angle">旋转角度,弧度制</param>
    ''' <param name="origin">旋转中心</param>
    Public Sub RotateTo(angle As Double, origin As ClsPoint2d)
        Me.Center.Rotate(angle, origin)
    End Sub

    ''' <summary>
    ''' 旋转生成新的圆
    ''' </summary>
    ''' <param name="angle">旋转角度,弧度制</param>
    ''' <param name="origin">旋转中心</param>
    Public Function Rotate(angle As Double, origin As ClsPoint2d) As ClsCircle

        Return New ClsCircle With
            {
                 .Center = Me.Center.Rotate(angle, origin),
                .Radius = Me.Radius
            }

    End Function
    ''' <summary>
    ''' 缩放当前直线获取新的圆 
    ''' </summary>
    ''' <param name="scaleFactor">缩放的系数，大于1表示放大，小于1表示缩小</param>
    ''' <param name="scaleCenter">缩放的基准点</param>
    ''' <returns>新的圆 </returns>
    Public Function Scaling(ByVal scaleFactor As Double, ByVal scaleCenter As ClsPoint2d) As ClsCircle
        Dim rtn As New ClsCircle With
            {
                 .Center = Me.Center.Scaling(scaleFactor, scaleCenter),
                .Radius = Me.Radius * scaleFactor
            }

        Return rtn
    End Function

    ''' <summary>
    ''' 镜像当前直线获取新的圆 
    ''' </summary>
    ''' <param name="mirrorP1">对称轴第1点</param>
    ''' <param name="mirrorP2">对称轴第2点</param>
    ''' <returns>按照对称轴对称后的圆</returns>
    Public Function Mirror(ByVal mirrorP1 As ClsPoint2d, ByVal mirrorP2 As ClsPoint2d) As ClsCircle
        Dim rtn As New ClsCircle With
            {
                 .Center = Me.Center.Mirror(mirrorP1, mirrorP2),
                .Radius = Me.Radius
            }
        Return rtn
    End Function

    ''' <summary>
    ''' 移动当前圆
    ''' </summary>
    ''' <param name="Displacement">移动目标点到初始点的向量</param>
    Public Sub MoveTo(Displacement As ClsVector2d)
        Me.Center.MoveTo(Displacement)
    End Sub
#End Region
End Class


